#include "stdafx.h"
#include "LiteConfig.h"


using namespace std;


SQLite4Config* SQLite4Config::GetConfig(std::string configname, bool liteconfig)
{
	return CLiteConfig::GetConfig(configname);
}


CLiteConfig::CLiteConfig(std::string configname) :CFileManager(configname)
{
}


CLiteConfig::~CLiteConfig()
{
}

CLiteConfig * CLiteConfig::GetConfig(std::string configname)
{
	InitialGlobalResource();
	CLiteConfig* obj = new CLiteConfig(configname);
	if (!obj) {
		LogInfo("Error: New CLiteConfig object failed!");
		return nullptr;
	}
	if (FindAndAddDBFile(configname, reinterpret_cast<uintptr_t>(obj)) == true) {
		if (obj->Initialize() == false) {

		}
	}
	return obj;
}

long CLiteConfig::Insert(std::string key, std::string value, bool unique, std::string item)
{
	long insertid = 0;
	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	string sqlcmd = "Insert into " + GetTableName() + " (Key, Value, Item) VALUES(\"" + key + "\", \"" + value + "\", " + item + ");" +
		"select count(ID) From " + GetTableName() + " where Key = \"" + key + "\" and value = \"" + value + "\" and Item = \"" + item + "\";" +
		"select ID From " + GetTableName() + " where Key = \"" + key + "\" and value = \"" + value + "\" and Item = \"" + item + "\"";
	if (sqlite3_exec(mSession, sqlcmd.c_str(), CLiteConfig::sqlite4configcallback, &args, &errmsg)) {
		LogInfo("Insert failed! %s\n", errmsg);
		return insertid;
	}

	DWORD dwRS = WaitForSingleObject(args.EventObj, 10);
	int rc = 0;
	switch (dwRS)
	{
	case WAIT_OBJECT_0://we got some result
		if (args.total == 0) {//Error 
			LogInfo("Error: not found insert result\n");
			break;
		}
		if (args.total > 1) {
			LogInfo("Warning: multiple result\n");
		}
		for each (string  id in args.values.begin()->second)
		{
			insertid = std::strtol(id.c_str(), NULL, 0);//if it's multiple result, we use the lastest
		}
		break;
	default://we're timeout here
		LogInfo("Insert And Select ID Timeout\n");
		break;
	}
	return insertid;
}

long CLiteConfig::update(std::string key, std::string value, std::string item)
{
	long updateid = 0;
	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	string sqlcmd = "Update " + GetTableName() + " SET Value = \"" + value + "\" where Key = \"" + key + "\" and Item = \"" + item + "\";" +
		"select count(ID) From " + GetTableName() + " where Key = \"" + key + "\" and value = \"" + value + "\" and Item = \"" + item + "\";" +
		"select ID From " + GetTableName() + " where Key = \"" + key + "\" and value = \"" + value + "\" and Item = \"" + item + "\"";
	if (sqlite3_exec(mSession, sqlcmd.c_str(), CLiteConfig::sqlite4configcallback, &args, &errmsg)) {
		LogInfo("Update failed! %s\n", errmsg);
		return updateid;
	}

	DWORD dwRS = WaitForSingleObject(args.EventObj, 10);
	int rc = 0;
	switch (dwRS)
	{
	case WAIT_OBJECT_0://we got some result
		if (args.total == 0) {//Error 
			LogInfo("Error: not found insert result\n");
			break;
		}
		if (args.total > 1) {
			LogInfo("Warning: multiple result\n");
		}
		for each (string  id in args.values.begin()->second)
		{
			updateid = std::strtol(id.c_str(), NULL, 0);//if it's multiple result, we use the lastest
		}
		break;
	default://we're timeout here
		LogInfo("Insert And Select ID Timeout\n");
		break;
	}
	return updateid;
}

std::string CLiteConfig::Find(std::string key, std::string item)
{
	string value = string();
	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	string sqlcmd = "select count(Value) From " + GetTableName() + " where Key = \"" + key + "\" ;" +
		"select Value From " + GetTableName() + " where Key = \"" + key + "\"";
	if (sqlite3_exec(mSession, sqlcmd.c_str(), CLiteConfig::sqlite4configcallback, &args, &errmsg)) {
		LogInfo("Find failed! %s\n", errmsg);
		return value;
	}

	DWORD dwRS = WaitForSingleObject(args.EventObj, 10);
	int rc = 0;
	switch (dwRS)
	{
	case WAIT_OBJECT_0://we got some result
		if (args.total == 0) {
			return value;
		}
		if (args.total > 1) {
			LogInfo("Warning: multiple result\n");
		}
		for each (string  val in args.values.begin()->second)
		{
			value = val;//if it's multiple result, we use the lastest
		}
		break;
	default://we're timeout here
		LogInfo("Find value Timeout\n");
		break;
	}
	return value;
}

int CLiteConfig::Remove(std::string key, std::string item)
{
	int ret = 0;
	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	string sqlcmd = "delete From " + GetTableName() + " where Key = \"" + key + "\" and Item = \"" + item + "\"";
	if (sqlite3_exec(mSession, sqlcmd.c_str(), CLiteConfig::sqlite4configcallback, &args, &errmsg)) {
		LogInfo("Delete failed! %s\n", errmsg);
		return ret;
	}
	ret = 0;
	return ret;
}

int CLiteConfig::sqlite4configcallback(void *args, int number, char ** value, char ** name)
{
	SQLiteCBArg* sqliteArgs = reinterpret_cast<SQLiteCBArg*>(args);
	if (sqliteArgs->total < 0) {//first return. this should for count(), because all select result should follow count() cmd, if no row we woun't be here
		sqliteArgs->total = atoi(value[0]);
		if (sqliteArgs->total == 0) {//maybe no result
			SetEvent(reinterpret_cast<SQLiteCBArg*>(args)->EventObj);
			return 0;
		}
		return 0;
	}
	if (sqliteArgs->total == 0) {//maybe timeout
		return 0;
	}
	if (number > 1) {//multiple column, it's not expected for CLiteConfig
		number = 1;
	}
	for (int i = 0; i < number; i++) {
		sqliteArgs->values[name[i]].push_back(value[i]);
	}
	if (sqliteArgs->values[name[0]].size() == sqliteArgs->total) {//total completed
		SetEvent(reinterpret_cast<SQLiteCBArg*>(args)->EventObj);
		return 0;
	}
	return 0;
}

bool CLiteConfig::Initialize()
{
	int ret = OpenDB(GetFileName());
	if ( ret != 0) {
		LogInfo("CLiteConfig::Initialize Open DBFile Failed with return code:%u", ret);
		return false;
	}

	SQLiteCBArg args(mSyncObj);
	char *errmsg = nullptr;
	if (sqlite3_exec(mSession, "SELECT count(name) FROM sqlite_master WHERE type='table';SELECT name FROM sqlite_master WHERE type='table'", sqlite4configcallback, &args, &errmsg)) {
		LogInfo("CLiteConfig::Initialize failed! %s\n", errmsg);
		return false;
	}

	DWORD dwRS = WaitForSingleObject(args.EventObj, 10);
	int rc = 0;
	switch (dwRS)
	{
	case WAIT_OBJECT_0://we got some result
		if (args.total == 0) {//empty db file
			string sqlcmd = "CREATE TABLE " + GetTableName() + " (ID INTEGER PRIMARY KEY AUTOINCREMENT, Key NUMERIC NOT NULL, Value NUMERIC NOT NULL, Item INTEGER NOT NULL)";
			rc = sqlite3_exec(mSession, sqlcmd.c_str(),
				sqlite4configcallback, &mSyncObj, &errmsg);
			if (rc) {
				LogInfo("CLiteConfig::Initialize Try to create Table with error:%u: %s\n", rc, errmsg);
			}
			break;
		}
		if (args.total > 2) {
			LogInfo("Warning: CLiteConfig should contain so much tables\n");
		}

		for each (string name in args.values.begin()->second)
		{
			if (name != "sqlite_sequence") {//this is not auto create table, we use it
				GetTableName() = name;//if it's multiple result, we use the lastest
			}
		}
		break;
	default://we're timeout here
		LogInfo("CLiteConfig::Initialize Initial DB File Timeout\n");
		break;
	}
	return true;
}
